from ete2 import Tree
import numpy
import math
import linecache
import os
from scipy.io.numpyio import fwrite

'''
-----------------------------------------------------------------------------------------------------------------
class PathProximity helps to calculate distances between pathes in the tree
INPUT: tree in the file self.tree
OUTPUT: call function CalculateDistance() and it will return a numpy matrix of distances
distances can be written to a file for example: numpy.save(output, distance)  and read numpy.load(output + '.npy') 
------------------------------------------------------------------------------------------------------------------
'''
class PathProximity(object):
    
    def __init__(self, filename, number_of_images):
        self.number_of_images = number_of_images
        self.tree = Tree(filename + '_tree.nw')
        
    def PathDistance(self, image1, image2):
        node1 = self.tree.search_nodes(name = str(image1))[0]
        node2 = self.tree.search_nodes(name = str(image2))[0]
        root = self.tree.get_tree_root()
        ancestor = self.tree.get_common_ancestor(node1, node2)
        
        root_ancestor = self.tree.get_distance(root, ancestor, topology_only=True)
        root_node1 = self.tree.get_distance(root, node1, topology_only=True)
        root_node2 = self.tree.get_distance(root, node2, topology_only=True)
        
        return 1-((2*root_ancestor)/(root_node1+root_node2))
    
    def CalculateDistances(self):
        imageSet1 = range(self.number_of_images)
        imageSet2 = range(self.number_of_images)
        path_distances = numpy.zeros((len(imageSet1), len(imageSet2)))
        for image1 in imageSet1:
            print 'image1 = ', image1
            i = 0
            for image2 in imageSet2:
                path_distances[image1, i] = self.PathDistance(image1, image2)
                i += 1
        return path_distances
    
filename = os.path.dirname(__file__) + '-files/' + 'features-100'
number_of_images = 100
pp = PathProximity(filename, number_of_images)
distance = pp.CalculateDistances()
numpy.save(filename + '_tree-distances', distance)
numpy.savetxt(filename + '_txt_tree-distances', distance)

class SimpleProximity(object):
    INPUT_PATH = os.path.dirname(__file__) + '/Input/'
    L = 0.5
    
    def __init__(self, filename, proximity_type, number_of_images):
        self.number_of_images = number_of_images
        self.proximity_type = proximity_type
        self.input = SimpleProximity.INPUT_PATH + filename + str(number_of_images)
        
    def PathDistance(self, image1, image2):
        
        vector1 = numpy.array(self.GetVector(image1))
        vector2 = numpy.array(self.GetVector(image2))
        
        if self.proximity_type == 'Euclidean':
            distance = math.sqrt(sum((vector1 - vector2)**2))
        elif self.proximity_type == 'Manhattan':
            distance = sum(abs(vector1-vector2))
        elif self.proximity_type == 'Maximum':
            distance = max(vector1-vector2) 
        elif self.proximity_type == 'Mahalanoibis':
            matrix = numpy.vstack((vector1,vector2)).T
            covariance = numpy.cov(matrix)
            distance = math.sqrt(numpy.dot(numpy.dot((vector1-vector2),numpy.linalg.pinv(covariance)),(vector1-vector2).T))
        elif self.proximity_type == 'Cosine':
            distance = 1-numpy.dot(vector1, vector2)/(math.sqrt(sum((vector1)**2))*math.sqrt(sum((vector2)**2)))
            
        distance = numpy.exp(-(distance)/(2*(SimpleProximity.L**2)))
        
        return distance
    
    def GetVector(self, number):
        # Get vector from line #number from file input
        # number starts from 1
        line = linecache.getline(self.input, number+1)
        return map(float, line.split())
    
    def SetDistances(self, imageSet1, imageSet2):
        distances = numpy.zeros((len(imageSet1), len(imageSet2)))
        for image1 in imageSet1:
            i = 0
            for image2 in imageSet2:
                distances[image1, i] = self.PathDistance(image1, image2)
                i += 1
        return distances
    
# Calculate kernel  
''' 
file = 'features_lewis'
number_of_images = 1000
output = os.path.dirname(__file__) + '/ProximityMatrix/' + file + str(number_of_images) + '_kernel.dist'
sp = SimpleProximity(file, 'Euclidean', number_of_images)
distancematrix = sp.SetDistances(range(number_of_images), range(number_of_images))

numpy.savetxt(output, distancematrix) '''
''' 
filename = 'features_lewis'
proximity_type = 'Euclidean'
clustering_type = 'kmeans'
number_of_images = 1000
  
pp = PathProximity(filename, proximity_type, clustering_type, number_of_images)
distance = pp.CalculateDistances()
#for image1 in range(number_of_images):
#    for image2 in range(number_of_images):
#        pp.path_distances[image1, image2] = pp.PathDistance(image1, image2)
#    print image1
output = os.path.dirname(__file__) + '/ProximityMatrix/'+str(number_of_images) +'_' + proximity_type + '_' + clustering_type + '.treekernel'
numpy.save(output, distance) 
print numpy.load(output + '.npy') 
'''